/*++
 Module Name:

    SensorDdi.cpp

Abstract:

      This module implements the ISensorDriver interface which is used
      by the Sensor Class Extension.
--*/

#include "internal.h"
#include "Defines.h"
#include "SensorDdi.h"
#include "sensorddi.tmh"
#include "DeviceProperty.h"
#include "PortableDeviceProps.h"
#include "ObjectLock.h"
#include "Sensor.h"
#include "SensorRegistry.h"
#include "ReadWriteRequest.h"
#include "AutoResetEvent.h"
#include "RequestContext.h"
#include "AccelerometerSensor.h"
#include "Client.h"
#include "DataQueue.h"
#include "Thread.h"
#include "ClientsManager.h"

// *************************************************************************
//
// CSensorDDI class
//
// ***************************************************************************



/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI()
//
// Constructor.
//
/////////////////////////////////////////////////////////////////////////
CSensorDDI::CSensorDDI()
{

}

/////////////////////////////////////////////////////////////////////////
//
// ~CSensorDDI()
//
// Destructor
//
/////////////////////////////////////////////////////////////////////////
CSensorDDI::~CSensorDDI()
{

}

/////////////////////////////////////////////////////////////////////////
//
// Initialize()
//
// Initialize function that will set up all the propertykeys
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI::Initialize(IWDFDevice* pDevice)
{
    HRESULT hr = (NULL == pDevice) ? E_INVALIDARG : S_OK;

#ifdef DBG
	PWSTR buffer;
	DWORD dwSize;
	USES_CONVERSION;

	hr = pDevice->RetrieveDeviceInstanceId(NULL, &dwSize);
	if(SUCCEEDED(hr))
	{
		buffer = new WCHAR[dwSize];
		hr = pDevice->RetrieveDeviceInstanceId(buffer, &dwSize);
		Trace(TRACE_LEVEL_INFORMATION, "CSensorDDI::Initialize() - InstanceId = %s", W2CA(buffer));
		delete [] buffer;
	}

	hr = pDevice->RetrieveDeviceName(NULL, &dwSize);
	if(SUCCEEDED(hr))
	{
		buffer = new WCHAR[dwSize];
		hr = pDevice->RetrieveDeviceName(buffer, &dwSize);
		Trace(TRACE_LEVEL_INFORMATION, "CSensorDDI::Initialize() - Device Name = %s", W2CA(buffer));
		delete [] buffer;
	}
#endif

    if (SUCCEEDED(hr))
    {
		//
        // Initialize all sensors
        //
		vector<CSensor *> sensors = CSensorRegistry::GetRegisteredSensors(false);
		for(vector<CSensor *>::const_iterator it = sensors.begin() ;
			it != sensors.end() ;
			++it)
		{
			hr = (*it)->Init(pDevice, this);
			if(FAILED(hr))
				return hr;
		}
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnClientConnect
//
// Routine Description:
//
//  This method is called by Sensor Class Extension when a client app connects
//  to a Sensor
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys 
//             are being requested
// 
//
// Return Value:
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnClientConnect(
        __in IWDFFile* pClientFile,
        __in LPWSTR pwszObjectID
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");

	//
	// validate input
	//
	if(pClientFile == NULL || pwszObjectID == NULL)
		return E_POINTER;

	//
	// add this client to the clients manager
	//
	return CClientsManager::GetClientsManager().AddClient(pClientFile, pwszObjectID);
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnClientDisconnect
//
// Routine Description:
//
//  This method is called by Sensor Class Extension when a client app disconnects
//  from a Sensor
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys 
//             are being requested
// 
//
// Return Value:
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnClientDisconnect(
        __in IWDFFile* pClientFile,
        __in LPWSTR pwszObjectID
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");

	//
	// validate input
	//
	if(pClientFile == NULL || pwszObjectID == NULL)
		return E_POINTER;

	//
	// remove this client from the clients manager
	//
	return CClientsManager::GetClientsManager().RemoveClient(pClientFile, pwszObjectID);
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnClientSubscribeToEvents
//
// Routine Description:
//
//  This method is called by Sensor Class Extension when a client subscribes to
//  events by calling SetEventSink
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys 
//             are being requested
// 
//
// Return Value:
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnClientSubscribeToEvents(
        __in IWDFFile* pClientFile,
        __in LPWSTR pwszObjectID
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");
    
	//
	// validate input
	//
	if(pClientFile == NULL || pwszObjectID == NULL)
		return E_POINTER;

	//
	// subscribe this client to events with the clients manager
	//
	return CClientsManager::GetClientsManager().SubscribeToEvents(pClientFile, pwszObjectID);
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnClientUnsubscribeFromEvents
//
// Routine Description:
//
//  This method is called by Sensor Class Extension when a client unsubscribes from
//  events by calling SetEventSink(NULL)
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys 
//             are being requested
// 
//
// Return Value:
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnClientUnsubscribeFromEvents(
        __in IWDFFile* pClientFile,
        __in LPWSTR pwszObjectID
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");

	//
	// validate input
	//
	if(pClientFile == NULL || pwszObjectID == NULL)
		return E_POINTER;

	//
	// unsubscribe this client from events with the clients manager
	//
	return CClientsManager::GetClientsManager().UnsubscribeFromEvents(pClientFile, pwszObjectID);
}

///////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnProcessWpdMessage
//
///////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnProcessWpdMessage(
        __in IUnknown* pPortableDeviceValuesParamsUnknown,
        __in IUnknown* pPortableDeviceValuesResultsUnknown
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");
    UNREFERENCED_PARAMETER(pPortableDeviceValuesParamsUnknown);
    UNREFERENCED_PARAMETER(pPortableDeviceValuesResultsUnknown);

    return S_OK;
}


/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnGetSupportedSensorObjects
//
// Routine Description:
//
//  This method is called by Sensor Class Extension during the initialize procedure to get 
//  the list of sensor objects and their supported properties.
//  
//  NOTE: This driver supports an accelerometer sensor.
//
// Arguments:
//
//  ppPortableDeviceValuesCollection - A double IPortableDeviceValuesCollection pointer
//                                     that receives the set of Sensor property values
//
// Return Value:
//
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI::OnGetSupportedSensorObjects(
        __out IPortableDeviceValuesCollection** ppSensorObjectCollection
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");

	//
	// validate input
	//
	if(ppSensorObjectCollection == NULL)
		return E_POINTER;

	CComPtr<IPortableDeviceValuesCollection>	spSensors;
	CComPtr<IPortableDeviceKeyCollection>		spKeys;
	CComPtr<IPortableDeviceValues>				spValues;
	HRESULT										hr;

	//
	// create the device values collection object
	//
	hr = spSensors.CoCreateInstance(CLSID_PortableDeviceValuesCollection);
	if(FAILED(hr))
		return hr;

	//
	// iterate through our sensors collection and get each one's
	// properties and values excluding the "DEVICE" pseudo-sensor
	// object
	//
	vector<CSensor *> sensors = CSensorRegistry::GetRegisteredSensors();
	for(vector<CSensor *>::const_iterator it = sensors.begin() ;
		it != sensors.end() ; ++it)
	{
		CSensor *pSensor = *it;

		//
		// get supported properties
		//
		hr = pSensor->GetSupportedProperties(&spKeys);
		if(SUCCEEDED(hr))
		{
			//
			// get the values for these properties
			//
			hr = pSensor->GetProperties(spKeys, &spValues);
			if(SUCCEEDED(hr))
				spSensors->Add(spValues);
		}
	}

	*ppSensorObjectCollection = spSensors.Detach();
	return hr;
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnGetSupportedProperties
//
// Routine Description:
//
//  This method is called by Sensor Class Extension to get the list of supported properties
//  for a particular Sensor.
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys are being requested
//  ppKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
//
// Return Value:
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI::OnGetSupportedProperties(
        __in  LPWSTR pwszObjectID,
        __out IPortableDeviceKeyCollection** ppKeys
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");

	//
	// validate inputs
	//
	if(pwszObjectID == NULL || ppKeys == NULL)
		return E_POINTER;

	//
	// get the sensor for the given ID
	//
	CSensor *pSensor = CSensorRegistry::GetSensorById(wstring(pwszObjectID));
	if(pSensor == NULL)
		return HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);

	//
	// get the supported properties
	//
	return pSensor->GetSupportedProperties(ppKeys);
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnGetSupportedDataFields
//
// Routine Description:
//
//  This method is called by Sensor Class Extension to get the list of supported data fields
//  for a particular Sensor.
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys are being requested
//  ppKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
//
// Return Value:
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnGetSupportedDataFields(
        __in  LPWSTR pwszObjectID,
        __out IPortableDeviceKeyCollection** ppKeys
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");

	//
	// validate inputs
	//
	if(pwszObjectID == NULL || ppKeys == NULL)
		return E_POINTER;

	//
	// get the sensor for the given ID
	//
	CSensor *pSensor = CSensorRegistry::GetSensorById(wstring(pwszObjectID));
	if(pSensor == NULL)
		return HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);

	//
	// get the supported properties
	//
	return pSensor->GetSupportedDataFields(ppKeys);
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnGetSupportedEvents
//
// Routine Description:
//
//  This method is called by Sensor Class Extension to get the list of supported events
//  for a particular Sensor.
//  
// Arguments:
//
// pwszObjectID - String that represents the object whose supported property keys are being requested
//  ppSupportedEvents - A set of GUIDs that represent the supported events
//  pulEventCount - Count of the number of events supported
//
// Return Value:
//  Status
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI::OnGetSupportedEvents(
        __in    LPWSTR pwszObjectID,
        __out   GUID** ppSupportedEvents,
        __out   ULONG* pulEventCount
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");

	//
	// validate input
	//
	if(NULL == pwszObjectID ||
       NULL == ppSupportedEvents ||
       NULL == pulEventCount)
    {
        return E_POINTER;
    }

    HRESULT hr = S_OK;

	//
	// see if we can locate this sensor
	//
	CSensor *pSensor = CSensorRegistry::GetSensorById(wstring(pwszObjectID));
	if(pSensor == NULL)
		return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);

	//
    // Allocate a buffer for one event type GUID.
    // The platform will free this memory.
	//
    GUID* pBuf = (GUID*)CoTaskMemAlloc(sizeof(GUID)*2);
    if(pBuf != NULL)
    {
        pBuf[0] = SENSOR_EVENT_DATA_UPDATED; // data event
        pBuf[1] = SENSOR_EVENT_STATE_CHANGED;

		//
        // Return the pointer to the buffer and the count.
		//
        *ppSupportedEvents = pBuf;
        *pulEventCount = 2;
    }
    else // Failed to allocate the GUID buffer.
    {
        hr = E_OUTOFMEMORY;

        *ppSupportedEvents = NULL;
        *pulEventCount = 0;
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnGetProperties
//
// Routine Description:
//
//  This method is called by Sensor Class Extension to get Sensor property values 
//  for a particular Sensor.
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys are being requested
// 
//
// Return Value:
// 
// ppValues - IPortableDeviceValues that contains the values for the keys requested
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnGetProperties(
        __in  IWDFFile* pClientFile,
        __in  LPWSTR pwszObjectID,
        __in  IPortableDeviceKeyCollection* pKeys,
        __out IPortableDeviceValues** ppValues
        )
{   
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");
    UNREFERENCED_PARAMETER(pClientFile);

	//
	// validate inputs
	//
	if(pwszObjectID == NULL ||
	   pKeys == NULL ||
	   ppValues == NULL)
	{
	   return E_POINTER;
	}

	//
	// get the sensor for the given ID
	//
	CSensor *pSensor = CSensorRegistry::GetSensorById(wstring(pwszObjectID));
	if(pSensor == NULL)
		return HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);

	//
	// get the supported properties
	//
	return pSensor->GetProperties(pKeys, ppValues);
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnGetDataFields
//
// Routine Description:
//
//  This method is called by Sensor Class Extension to get Sensor data values 
//  for a particular Sensor.
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys are being requested
// 
//
// Return Value:
// 
//  ppValues - IPortableDeviceValues that contains the values for the keys requested
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnGetDataFields(
        __in  IWDFFile* pClientFile,
        __in  LPWSTR pwszObjectID,
        __in  IPortableDeviceKeyCollection* pKeys,
        __out IPortableDeviceValues** ppValues
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");
    UNREFERENCED_PARAMETER(pClientFile);

	//
	// validate inputs
	//
	if(pwszObjectID == NULL ||
	   pKeys == NULL ||
	   ppValues == NULL)
	{
	   return E_POINTER;
	}

	//
	// get the sensor for the given ID
	//
	CSensor *pSensor = CSensorRegistry::GetSensorById(wstring(pwszObjectID));
	if(pSensor == NULL)
		return HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);

	//
	// get the supported properties
	//
	return pSensor->GetDataFields(pKeys, ppValues);
}

/////////////////////////////////////////////////////////////////////////
//
// CSensorDDI::OnSetProperties
//
// Routine Description:
//
//  This method is called by Sensor Class Extension to set Sensor property values 
//  for a particular Sensor.
//  
// Arguments:
//
//  pwszObjectID - String that represents the object whose supported property keys are being requested
// 
//
// Return Value:
// 
//  ppResults - IPortableDeviceValues that contains the results of the set operation
// 
//
/////////////////////////////////////////////////////////////////////////
HRESULT CSensorDDI:: OnSetProperties(
        __in IWDFFile* pClientFile,
        __in LPWSTR pwszObjectID,
        __in IPortableDeviceValues* pValues,
        __out IPortableDeviceValues** ppResults
        )
{
	Trace(TRACE_LEVEL_INFORMATION, "%!FUNC! Entry");
	UNREFERENCED_PARAMETER(pClientFile);
	UNREFERENCED_PARAMETER(pwszObjectID);
	UNREFERENCED_PARAMETER(pValues);
	UNREFERENCED_PARAMETER(ppResults);

	return S_OK;
}

